package org.korosoft.rusalad.syncnrun;

import org.apache.commons.cli.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.korosoft.rusalad.syncnrun.tools.InsecureSocketFactory;
import org.korosoft.rusalad.syncnrun.tools.KeyStore;
import org.korosoft.rusalad.syncnrun.tools.SecureSocketFactory;
import org.korosoft.rusalad.syncnrun.tools.SocketFactory;

import java.io.File;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Collection;

/**
 * Syncnrun daemon control class
 *
 * @author Dmitry Korotkov
 * @since 1.0
 */
public class Daemon {
    private static final Log log = LogFactory.getLog(Daemon.class);

    public static void main(String[] args) {

        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                log.debug(e.getMessage(), e);
                log.error(e.getMessage());
            }
        });

        final Options options = new Options();

        options.addOption("p", "port", true, String.format("Specify syncrun listening port (default is %d)", Main.DEFAULT_PORT));
        options.addOption(null, "insecure", false, "Start insecure server (without authentication/encryption)");
        options.addOption(null, "keystore", true, "Path to key store. Default is ~/.syncnrun");
        options.addOption("v", "verbose", false, "Verbose output");
        options.addOption(null, "silent", false, "Suppress output");
        options.addOption(null, "debug", false, "Debug output");

        CommandLineParser parser = new PosixParser();
        CommandLine commandLine = null;
        try {
            commandLine = parser.parse(options, args);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }

        if (commandLine.hasOption("verbose")) {
            Logger.getRootLogger().setLevel(Level.DEBUG);
        }

        if (commandLine.hasOption("silent")) {
            Logger.getRootLogger().setLevel(Level.OFF);
        }

        if (commandLine.hasOption("debug")) {
            Logger.getRootLogger().setLevel(Level.TRACE);
        }

        int port = Main.DEFAULT_PORT;
        final SocketFactory socketFactory;
        if (commandLine.hasOption("insecure")) {
            socketFactory = new InsecureSocketFactory();
        } else {
            final KeyStore keyStore;
            if (commandLine.hasOption("keystore")) {
                keyStore = new KeyStore(new File(commandLine.getOptionValue("keystore")));
            } else {
                keyStore = new KeyStore(new File(System.getProperty("user.home"), ".syncnrun"));
            }
            final PrivateKey privateKey = keyStore.getPrivateKey();
            final Collection<PublicKey> publicKeys = keyStore.getPublicKeys();
            if (publicKeys.size() == 0) {
                log.error(String.format("No client public keys found. Nobody will be able to connect."));
                System.exit(1);
            }
            socketFactory = new SecureSocketFactory(privateKey, publicKeys);
        }
        if (commandLine.hasOption("p")) {
            port = Integer.parseInt(commandLine.getOptionValue("p"));
        }
        Server server = new Server(socketFactory);
        try {
            server.start(port);
            server.await();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
